Skip to content

Conversation

@ngholiza
Copy link
Collaborator

Summary

Adds Microsoft Teams notifications for events that need an administrator, and a way for users to request a different role.

Notifications (Teams)

  • Posted to Teams through a Power Automate webhook (ADMIN_TEAMS_WEBHOOK_URL, the same Adaptive Card flow the recipe-proposal alerts use) when:
    • a user signs in for the first time, including a previously invited user;
    • equipment enters review: a new registration or changes by a non-admin, once per review cycle;
    • a user requests a role change.
  • Each event is written to a platform_notifications outbox in the same transaction as the event, with a unique key, so it can't be lost or duplicated. The sign-in check runs on every page load but notifies once per user.
  • Delivery runs in the background: each row is claimed individually with a lease and claim token, retried with backoff and bounded attempts, and stored errors are sanitized so webhook URLs never appear.
  • Email (SMTP or a Power Automate email flow) is supported but off until configured; for now Teams is the only channel.

Role change requests

  • Users: Settings → "Your role". Choose a role and explain why; one pending request at a time, can cancel, at most 3 per day.
  • Admins: a new Admin → Role Requests page to approve or reject, with an optional note.
  • Approval uses the same rules and audit log as the Users page role editor, which now shares that code. The requester sees the decision on their Settings page, and the new role applies on their next page load.
  • Access: row-level security keeps requests visible to their owner and to admins only.

Security fix (existing hole)

  • The web proxy forwarded GET /api/admin/users/{email}/role, the sign-in identity sync, using the system token, so any signed-in user could create or merge someone else's account. The proxy now refuses that route, and the API rejects calls to it that carry a user identity.

Also

  • Invited users stay "invited" when an admin edits their role before they sign in, so their first sign-in still notifies.
  • When duplicate accounts are merged, their role requests move to the remaining account.
  • /api/health reports whether each notification channel is configured.
  • The setup guide is api/ADMIN_NOTIFICATIONS.md.

Deploying

  1. Run the dt-db-migrate Job with the new image; it creates platform_notifications and role_change_requests and their access rules.
  2. Roll out the API and web images.
  3. Set PLATFORM_BASE_URL for the "Open in the platform" links. ADMIN_TEAMS_WEBHOOK_URL is already in dt-api-secret.

Test plan

  • New database integration suite (api/tests/test_admin_notifications_pg.py): 23/23 on a disposable Postgres 15. It covers first sign-in (once only, invited users, blocked proxied calls), equipment review cycles, the role-request lifecycle with audit, conflict and daily-limit errors, row-level security, delivery (Teams card, email webhook and SMTP, partial refusal, skipped channels, retry/backoff/give-up, stale-claim protection, sanitized errors) and account merging.
  • Existing RLS suite 36/36; unit suite 153 passed.
  • Web: tsc, eslint and next build pass.
  • Reviewed by a second agent (Codex) over 5 rounds; all findings resolved, final verdict approve.

🤖 Generated with Claude Code

- Durable notification outbox (platform_notifications), written in the same
  transaction as each event and delivered in the background to a Teams
  Power Automate webhook and to email (SMTP or a Power Automate email flow),
  with dedupe keys, leases, retries and backoff.
- Notify admins when a user signs in for the first time and when equipment
  (new or changed) is waiting for approval.
- Role change requests: users ask for a role from Settings; admins are
  notified and approve or reject on a new Role Requests page; approval uses
  the same rules and audit as the role editor, and the requester is emailed
  the decision. Row-level security keeps requests private to their owner
  and admins.
- Health reports whether each notification channel is configured.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Claim and lease one notification at a time and record results only while
  the claim token is held; retry only SMTP-refused recipients; store and log
  sanitized error categories; refuse unknown SMTP_SECURITY values; one-line
  subjects; at most one fast-path delivery thread per process; delete
  delivered rows after the retention period.
- Limit role requests to three per user per day and equipment review
  notifications to one per equipment per 15 minutes.
- Refuse the sign-in identity sync through the browser proxy and when a user
  identity is attached, so users cannot create or merge other accounts.
- Keep invited users invited when their role is edited, so their first
  sign-in still notifies; move role requests when accounts are merged.
- Enable RLS on role_change_requests before granting access.
- Show load failures (with retry) instead of empty request lists.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Notify admins each time equipment enters review; coalesce resubmissions
  while it is still pending instead of dropping alerts inside a time window.
  The registration row is locked so concurrent submissions serialize.
- Lock the user row before checking the daily role-request quota.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The platform will run with Teams notifications only for now, so a role
decision is not emailed. The Settings card and admin page no longer promise
an email; requesters see the decision on their Settings page. Email support
stays available and is used only when configured.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign in to join this conversation on GitHub.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant